// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Code style modification
// ------------------------------------------------------------------

namespace evbs {
namespace ertps {
namespace rtps {

bool RTPSMessageCreator::addMessageAcknack(CDRMessage_t* msg, const GuidPrefix_t& guidprefix,
                                           const GuidPrefix_t& remoteGuidPrefix, const EntityId_t& readerId,
                                           const EntityId_t& writerId, const SequenceNumberSet_t& SNSet, int32_t count,
                                           bool finalFlag) {
    (void)RTPSMessageCreator::addHeader(msg, guidprefix);
    (void)RTPSMessageCreator::addSubmessageInfoDST(msg, remoteGuidPrefix);
    (void)RTPSMessageCreator::addSubmessageAcknack(msg, readerId, writerId, SNSet, count, finalFlag);
    msg->length = msg->pos;
    return true;
}

bool RTPSMessageCreator::addSubmessageAcknack(CDRMessage_t* msg, const EntityId_t& readerId, const EntityId_t& writerId,
                                              const SequenceNumberSet_t& SNSet, int32_t count, bool finalFlag) {
    octet flags = 0x0U;
    Endianness_t old_endianess = msg->msg_endian;
#if EDDS_IS_BIG_ENDIAN_TARGET
    msg->msg_endian = BIGEND;
#else
    flags = flags | BIT(0);
    msg->msg_endian = LITTLEEND;
#endif  // if EDDS_IS_BIG_ENDIAN_TARGET

    if (finalFlag) {
        flags = flags | BIT(1);
    }

    // Submessage header.
    (void)CDRMessage::addOctet(msg, ACKNACK);
    (void)CDRMessage::addOctet(msg, flags);
    uint32_t submessage_size_pos = msg->pos;
    uint16_t submessage_size = 0U;
    (void)CDRMessage::addUInt16(msg, submessage_size);
    uint32_t position_size_count_size = msg->pos;

    (void)CDRMessage::addEntityId(msg, &readerId);
    (void)CDRMessage::addEntityId(msg, &writerId);
    // Add Sequence Number
    (void)CDRMessage::addSequenceNumberSet(msg, &SNSet);
    (void)CDRMessage::addInt32(msg, count);

    // TODO(Ricardo) Improve.
    submessage_size = uint16_t(msg->pos - position_size_count_size);
    octet* o = (octet*)&submessage_size;

    if (msg->buffer == nullptr || submessage_size_pos + 1 >= msg->max_size) {
        return false;
    }

    if (msg->msg_endian == DEFAULT_ENDIAN) {
        msg->buffer[submessage_size_pos] = *(o);
        msg->buffer[submessage_size_pos + 1U] = *(o + 1);
    } else {
        msg->buffer[submessage_size_pos] = *(o + 1);
        msg->buffer[submessage_size_pos + 1U] = *(o);
    }

    msg->msg_endian = old_endianess;

    return true;
}

bool RTPSMessageCreator::addMessageNackFrag(CDRMessage_t* msg, const GuidPrefix_t& guidprefix,
                                            const GuidPrefix_t& remoteGuidPrefix, const EntityId_t& readerId,
                                            const EntityId_t& writerId, SequenceNumber_t& writerSN,
                                            FragmentNumberSet_t fnState, int32_t count) {
    (void)RTPSMessageCreator::addHeader(msg, guidprefix);
    (void)RTPSMessageCreator::addSubmessageInfoDST(msg, remoteGuidPrefix);
    (void)RTPSMessageCreator::addSubmessageNackFrag(msg, readerId, writerId, writerSN, fnState, count);
    msg->length = msg->pos;
    return true;
}

bool RTPSMessageCreator::addSubmessageNackFrag(CDRMessage_t* msg, const EntityId_t& readerId,
                                               const EntityId_t& writerId, const SequenceNumber_t& writerSN,
                                               FragmentNumberSet_t fnState, int32_t count) {
    octet flags = 0x0U;
    Endianness_t old_endianess = msg->msg_endian;
#if EDDS_IS_BIG_ENDIAN_TARGET
    msg->msg_endian = BIGEND;
#else
    flags = flags | BIT(0);
    msg->msg_endian = LITTLEEND;
#endif  // if EDDS_IS_BIG_ENDIAN_TARGET

    // Submessage header.
    (void)CDRMessage::addOctet(msg, NACK_FRAG);
    (void)CDRMessage::addOctet(msg, flags);
    uint32_t submessage_size_pos = msg->pos;
    uint16_t submessage_size = 0U;
    (void)CDRMessage::addUInt16(msg, submessage_size);
    uint32_t position_size_count_size = msg->pos;

    (void)CDRMessage::addEntityId(msg, &readerId);
    (void)CDRMessage::addEntityId(msg, &writerId);
    // Add Sequence Number
    (void)CDRMessage::addSequenceNumber(msg, &writerSN);
    // Add fragment number status
    (void)CDRMessage::addFragmentNumberSet(msg, &fnState);
    (void)CDRMessage::addUInt32(msg, static_cast<uint32_t>(count));

    // TODO(Ricardo) Improve.
    submessage_size = uint16_t(msg->pos - position_size_count_size);
    octet* o = (octet*)&submessage_size;

    if (msg->buffer == nullptr || submessage_size_pos + 1 >= msg->max_size) {
        return false;
    }

    if (msg->msg_endian == DEFAULT_ENDIAN) {
        msg->buffer[submessage_size_pos] = *(o);
        msg->buffer[submessage_size_pos + 1U] = *(o + 1);
    } else {
        msg->buffer[submessage_size_pos] = *(o + 1);
        msg->buffer[submessage_size_pos + 1U] = *(o);
    }

    msg->msg_endian = old_endianess;

    return true;
}

}  // namespace rtps
}  // namespace ertps
}  // namespace evbs
